-
Notifications
You must be signed in to change notification settings - Fork 14
Oev 671 txm improvements #294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
8276ab6 to
64f4d2f
Compare
| go t.broadcastLoop(address, triggerCh) | ||
| go t.backfillLoop(address) | ||
| t.wg.Add(1) | ||
| go t.loop(address, triggerCh) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you remind me what the advantage was of combining these two loops?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It enables easier nonce management via error handling when broadcasting a transaction.
| return err | ||
| } | ||
|
|
||
| if tx == nil || *tx.Nonce != latestNonce { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when does this scenario happen? hard for me to think of why the local nonce would be behind the chain nonce if it has to originate from the local txm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not behind but in front. Here's an example: you broadcast tx A to the mempool. You restart the TXM, fetch the pending nonce, which is A+1, and start sending more transactions to the mempool. Since you restarted the TXM, transaction A is not being tracked. If it gets dropped by the RPC or the builders, TXM won't be able to recover unless it fills the nonce. That's what this case does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed TXM uses pending for the block parameter to capture pending transactions.
Does the Flashbots client behave accordingly (i.e., takes into account inflight transactions)?
pkg/txm/attempt_builder.go
Outdated
| // TODO: add better handling | ||
| bumpedAttempt, err := a.NewBumpAttempt(ctx, lggr, tx, *attempt) | ||
| if err != nil { | ||
| return attempt, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we select for that specific error so we reduce possibility of halting on a different error?
augustbleeds
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes look good to me, just want to clarify the error handling behavior for empty tx gas bumping.
29a1813 to
115e871
Compare
| } | ||
|
|
||
| func (e *errorHandler) HandleError(ctx context.Context, tx *types.Transaction, txErr error, txStore txm.TxStore, setNonce func(common.Address, uint64), isFromBroadcastMethod bool) error { | ||
| // If this isn't the first broadcast, don't mark the tx as fatal as other txs might be included on-chain. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // If this isn't the first broadcast, don't mark the tx as fatal as other txs might be included on-chain. | |
| // Only if this is the first broadcast, mark the tx as fatal. In other cases, other txs might be included on-chain |
|
|
||
| // bump purge attempts | ||
| if tx.IsPurgeable { | ||
| // TODO: add better handling |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to make a separate ticket with an explanation of what would be better here
| for { | ||
| bumpedAttempt, err := a.NewBumpAttempt(ctx, lggr, tx, *attempt) | ||
| if err != nil { | ||
| if errors.Is(err, fees.ErrConnectivity) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm looking at the error choice. I believe this is due to the code located here
if bumpedMaxPriorityFeePerGas.Cmp(priorityFeeThreshold) > 0 {
return ..., ErrConnectivity
}
It feels slightly counterintuitive to return ErrConnectivity for a value check, where ErrBumpFeeExceedsLimit might seem more natural.
Is the intention to treat priorityFeeThreshold as a sanity guardrail (implying the network/estimator is broken if fees get this high) rather than a budget limit (implying the tx just needs to pay more)? If so, adding a small comment explaining why this triggers a 'connectivity' error would be helpful for readability.
| defer cancel() | ||
| broadcastWithBackoff := newBackoff(1 * time.Second) | ||
| var broadcastCh <-chan time.Time | ||
| backfillCh := time.After(utils.WithJitter(t.config.BlockTime)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utils.WithJitter is a deprecated function.
It says to use timeutils package instead.
| metrics, err := NewMetaMetrics(chainID.String()) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to create Meta metrics: %w", err) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the MetaClient currently active -- it looks A-specific?
I noticed a potential dependency in the metrics setup. If beholder is unavailable, will this cause the client to error out as well?
Ideally, we want to ensure the client is decoupled so that a beholder outage doesn't block the main flow. For example, on 10/10 I saw many alerts about beholder potentially having issues.
| return NewFlashbotsClient(client, keyStore, url), nil | ||
| return NewFlashbotsClient(client, keyStore, url), nil, nil | ||
| default: | ||
| return NewMetaClient(lggr, client, keyStore, url, chainID) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code could then be made cleaner if NewMetaClient does not fail.
|
|
||
| // TODO: for now do the simple thing and drop the transaction instead of adding it to the fatal queue. | ||
| delete(m.UnconfirmedTransactions, *txToMark.Nonce) | ||
| delete(m.Transactions, txToMark.ID) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a few safety concerns about deleting these entries:
- Nonce Gaps: Could deleting this nonce create a gap in our internal inflight list? I'm concerned that higher-nonce transactions currently inflight might stall indefinitely waiting for the tx associated with this nonce to fill.
- Panic Risk: Dereferencing *txToMark.Nonce without checking if txToMark.Nonce is not nil can cause a panic.
- Less important but still relevant: could this complicate debugging by deleting this information?
| FromAddress: m.address, | ||
| ToAddress: common.Address{}, | ||
| Value: big.NewInt(0), | ||
| SpecifiedGasLimit: gasLimit, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: We could simplify this by hardcoding the gas limit to 21_000.
Since a standard ETH transfer to an EOA (0x0) has a fixed cost (21_000), passing in gasLimit and maintaining a separate EmptyTxLimitDefault config seems like unnecessary overhead. In addition, it would also remove the need to add gasEstimatorConfig parameter to func NewTxmV2.
If we were to instead send ETH to/from a smart contract, only then would the gas amount differ.
Each commit corresponds to the following features: